package jpsearch;

import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.sanselan.ImageReadException;
import org.apache.sanselan.ImageWriteException;
import org.apache.sanselan.common.byteSources.ByteSource;
import org.apache.sanselan.common.byteSources.ByteSourceFile;

public class JPSearchRewriter extends JpegRewriter implements JPSearchConstants{

	public void testUpdateJPSearchData() throws Exception{

		final File imageFile = new File("JPSImage.jpg");    
		String xml = new String("<?xml version=\"1.0\" encoding=\"UTF-8\"?>" +
				"<ImageDescription xmlns=\"JPSearch:schema:coremetadata\" "  +
				"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " +
				"xsi:schemaLocation=\"JPSearch:schema:coremetadata jpcore.xsd\">" +
				"<Identifier></Identifier> " +
				"<GPSPositioning latitude=\"0\" longitude=\"0\" altitude=\"0\"/>" +
				"<RegionOfInterest>" +
				"<RegionLocator>" +
				"<Region dim=\"2\">777 290 777 350</Region>" +
				"</RegionLocator>" +
				"<Description>Ivan Ivanov</Description>" +
				"<Keyword></Keyword>" +
				"</RegionOfInterest>" + 
				"</ImageDescription>");
		updateJPSearchData(imageFile, new File("Output.jpg"), xml);

		/*List md = new ArrayList();
			md.add(1); //version
			md.add(JPSEARCH_SCHEMA); //schema
			md.add(55); //confidence
			md.add("2013/02/26-16:17:00"); //creation date
			md.add("2013/02/27-17:17:00"); //last update date time
			md.add("Nicolas"); //author
			md.add(0); //flagRead 
			md.add(84); //encoding

			updateJPSearchData(imageFile, os, md, xml);*/

	}

	public void updateJPSearchData(File src, File dst, String data)
			throws ImageReadException, IOException, ImageWriteException {
		OutputStream os = null;
		os = new FileOutputStream(dst);
		os = new BufferedOutputStream(os);
		ByteSource byteSource = new ByteSourceFile(src);
		updateJPSearchData(byteSource, os, data);
	}

	public void updateJPSearchData(File src, OutputStream os, List metadata, String data)
			throws ImageReadException, IOException, ImageWriteException {
		ByteSource byteSource = new ByteSourceFile(src);
		updateJPSearchMetadata(byteSource, os, metadata, data);
	}

	/**
	 * Reads a Jpeg image, replaces the JPSearch XML and writes the result to a stream.
	 * 
	 * @param byteSource
	 *            ByteSource containing Jpeg image data.
	 * @param os
	 *            OutputStream to write the image to.
	 * @param xmpXml
	 *            String containing JPSearch XML.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void updateJPSearchData(ByteSource byteSource, OutputStream os, String data) throws ImageReadException, IOException,
	ImageWriteException
	{
		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
		List pieces = jfifPieces.pieces;
		pieces = removeJPSearchSegments(pieces);

		List newPieces = new ArrayList();
		byte JPSearchMetadataBytes[] = data.getBytes("utf-8");
		int index = 0;
		while (index < JPSearchMetadataBytes.length)
		{
			int segmentSize = Math.min(JPSearchMetadataBytes.length, MAX_SEGMENT_SIZE);

			JPSearchParser jparser = new JPSearchParser(byteSource);
			byte [] md = jparser.getJPSearchSegmentNativeMetadata();//get native metadata		
			
			byte segmentData[] = writeJPSearchSegment(md, JPSearchMetadataBytes, 0, JPSearchMetadataBytes.length);
			newPieces.add(new JFIFPieceSegment(JPEG_APP3_Marker, segmentData));
			index += segmentSize;
		}

		pieces = insertAfterLastAppSegments(pieces, newPieces);

		writeSegments(os, pieces);
	}

	/**
	 * Replaces the JPSearch metadata + XML and writes the result to a stream.
	 * 
	 * @param byteSource
	 *            ByteSource of new Jpeg image data.
	 * @param os
	 *            OutputStream to write the image to.            
	 * @param metadata 			  
	 *            List containing the basic metadata.
	 * @param data
	 *            String containing JPSearch XML.
	 */
	@SuppressWarnings({ "unchecked", "rawtypes" })
	public void updateJPSearchMetadata(ByteSource byteSource, OutputStream os, List metadata, String data)
			throws ImageReadException, IOException,
			ImageWriteException
			{
		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
		List pieces = jfifPieces.pieces;
		pieces = removeJPSearchSegments(pieces);

		List newPieces = new ArrayList();		
		JPSearchSegment jpSegment = new JPSearchSegment(metadata, data); //Creates a new segment with input native + XML metadata
		jpSegment.setData(data);
		byte [] xmlMetadata = jpSegment.getRawXMLMetadata();
		byte [] nativeMetadata = jpSegment.getRawNativeMetadata();

		byte segmentData[] = writeJPSearchSegment(nativeMetadata, xmlMetadata, 0, xmlMetadata.length);
		newPieces.add(new JFIFPieceSegment(JPEG_APP3_Marker, segmentData));

		pieces = insertAfterLastAppSegments(pieces, newPieces);

		writeSegments(os, pieces);
			}

	/**
	 * Remove the whole JPSearch segment from file
	 * 
	 * @param byteSource
	 * @param os
	 * @throws ImageReadException
	 * @throws IOException
	 * @throws ImageWriteException
	 */
	public void removeJPSearchSegment(ByteSource byteSource, OutputStream os) 
			throws ImageReadException, IOException, ImageWriteException
			{
		JFIFPieces jfifPieces = analyzeJFIF(byteSource);
		List pieces = jfifPieces.pieces;
		pieces = removeJPSearchSegments(pieces);
		writeSegments(os, pieces);
			}

	/**
	 * Writes a JPSearch metadata segment followed by the XML data.
	 * Returns what is writen to the Output Stream
	 * 
	 * @param metadata
	 * 			Metadata segment of JPSearch file i.e. all preceding of XML data
	 * @param data
	 * 			XML Data
	 * @param start
	 * 			Offset
	 * @param length 
	 * 			The size of data
	 *
	 */
	private byte[] writeJPSearchSegment(byte metadata[], byte data[], int start, int length) throws IOException
	{
		ByteArrayOutputStream os = new ByteArrayOutputStream();

		//Write JPSearch metadata
		os.write(metadata, 0, metadata.length);

		//Write JPSearch XML data
		os.write(data, start, length);

		return os.toByteArray();
	}
}
